///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Contains code to compute the minimal bounding sphere.
*	\file		LaborBoundingSphere.h
*	\author		Pierre Terdiman
*	\date		January, 29, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __LABORBOUNDINGSPHERE_H__
#define __LABORBOUNDINGSPHERE_H__
#include "LaborAABB.h"
#include "../LaborCoreDef.h"
NAMESPACE_LABOR3D_BEGIN

enum eLAxes
{
	BS_NONE,
	BS_GEMS,
	BS_MINIBALL,

	BS_FORCE_DWORD	= 0x7fffffff
};

class  LABORCORE_API CLSphere
{
public:
	//! Constructor
	inline					CLSphere()																		{}
	//! Constructor
	inline					CLSphere(const CLPoint3& center, float radius) : mCenter(center), mRadius(radius)	{}
	//! Constructor
	CLSphere(unsigned int nb_verts, const CLPoint3* verts);
	//! Copy constructor
	inline					CLSphere(const CLSphere& sphere) : mCenter(sphere.mCenter), mRadius(sphere.mRadius)	{}
	//! Destructor
	inline					~CLSphere()																		{}

	CLAxes	compute(unsigned int nb_verts, const CLPoint3* verts);
	bool			fastCompute(unsigned int nb_verts, const CLPoint3* verts);

	// Access methods
	inline	const CLPoint3&	getCenter()						const		{ return mCenter; }
	inline	float			getRadius()						const		{ return mRadius; }

	inline	const CLPoint3&	center()						const		{ return mCenter; }
	inline	float			radius()						const		{ return mRadius; }

	inline	CLSphere&			set(const CLPoint3& center, float radius)		{ mCenter = center; mRadius = radius; return *this; }
	inline	CLSphere&			setCenter(const CLPoint3& center)				{ mCenter = center; return *this; }
	inline	CLSphere&			setRadius(float radius)						{ mRadius = radius; return *this; }

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Tests if a point is contained within the sphere.
	*	\param		p	[in] the point to test
	*	\return		true if inside the sphere
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	bool			contains(const CLPoint3& p)		const
	{
		return mCenter.squareDistance(p) <= mRadius*mRadius;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Tests if a sphere is contained within the sphere.
	*	\param		sphere	[in] the sphere to test
	*	\return		true if inside the sphere
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	bool			contains(const CLSphere& sphere)	const
	{
		// If our radius is the smallest, we can't possibly contain the other sphere
		if(mRadius < sphere.mRadius)	return false;
		// So r is always positive or null now
		float r = mRadius - sphere.mRadius;
		return mCenter.squareDistance(sphere.mCenter) <= r*r;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Tests if a box is contained within the sphere.
	*	\param		aabb	[in] the box to test
	*	\return		true if inside the sphere
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline bool			contains(const CLAABB& aabb)	const
	{
		// I assume if all 8 box vertices are inside the sphere, so does the whole box.
		// Sounds ok but maybe there's a better way?
		float R2 = mRadius * mRadius;
#ifdef USE_MIN_MAX
		const CLPoint3& Max = ((xlShadowAABB&)&aabb).mMax;
		const CLPoint3& Min = ((xlShadowAABB&)&aabb).mMin;
#else
		CLPoint3 Max; aabb.getMax(Max);
		CLPoint3 Min; aabb.getMin(Min);
#endif
		CLPoint3 p;
		p.x=Max.x; p.y=Max.y; p.z=Max.z;	if(mCenter.squareDistance(p)>=R2)	return false;
		p.x=Min.x;							if(mCenter.squareDistance(p)>=R2)	return false;
		p.x=Max.x; p.y=Min.y;				if(mCenter.squareDistance(p)>=R2)	return false;
		p.x=Min.x;							if(mCenter.squareDistance(p)>=R2)	return false;
		p.x=Max.x; p.y=Max.y; p.z=Min.z;	if(mCenter.squareDistance(p)>=R2)	return false;
		p.x=Min.x;							if(mCenter.squareDistance(p)>=R2)	return false;
		p.x=Max.x; p.y=Min.y;				if(mCenter.squareDistance(p)>=R2)	return false;
		p.x=Min.x;							if(mCenter.squareDistance(p)>=R2)	return false;

		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Tests if the sphere intersects another sphere
	*	\param		sphere	[in] the other sphere
	*	\return		true if spheres overlap
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	bool			intersect(const CLSphere& sphere)	const
	{
		float r = mRadius + sphere.mRadius;
		return mCenter.squareDistance(sphere.mCenter) <= r*r;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Checks the sphere is valid.
	*	\return		true if the box is valid
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	bool			isValid()	const
	{
		// Consistency condition for spheres: Radius >= 0.0f
		if(mRadius < 0.0f)	return false;
		return true;
	}
public:
	CLPoint3			mCenter;		//!< CLSphere center
	float			mRadius;		//!< CLSphere radius

};

NAMESPACE_LABOR3D_END

#endif // __LABORBOUNDINGSPHERE_H__
